use std::str::FromStr;

// 气温在25℃时的声速，单位mm/s
const SOUND_SPEED: f64 = 345700.0;

// A1的标准音高
const PITCH: f64 = 440.0;

// 12个音名
pub const NOTES: [&'static str; 12] = [
    "A", "bB", "B", "C", "bD", "D", "bE", "E", "F", "bG", "G", "bA",
];

pub fn compute_note_frequency(note: &str) -> Option<f64> {
    NOTES.iter().position(|&n| n == note).map(|index| {
        let ratio = 2.0_f64.powf(1.0 / 12.0);
        PITCH * ratio.powi(index as i32)
    })
}

pub fn compute_base_frequency(base_name: char, tune_frequency: f64) -> f64 {
    match base_name {
        '5' => tune_frequency * 6.0 / 8.0,
        '2' => tune_frequency * 9.0 / 16.0,
        _ => todo!("不支持全按为{}的指法", base_name),
    }
}

pub fn compute_lengths(
    base_name: char,
    diameter: f64, // 管径，单位mm
    base_frequency: f64,
    frequencies: Option<String>,
) -> Vec<Vec<String>> {
    let steps = get_steps(base_name);
    let frequencies_provided = frequencies.is_some();
    let freqs = frequencies
        .map(|nums| {
            nums.split(',')
                .map(|n| f64::from_str(n.trim()).unwrap())
                .collect()
        })
        .unwrap_or(
            steps
                .iter()
                .map(|(step, _, _)| base_frequency * step)
                .collect::<Vec<_>>(),
        );

    let eff_lens = freqs
        .iter()
        .map(|&f| effective_length(f))
        .collect::<Vec<_>>();
    let act_lens = eff_lens
        .iter()
        .enumerate()
        .map(|(index, &len)| actual_length(len, diameter, index == 0))
        .collect::<Vec<_>>();
    let diffs = act_lens
        .iter()
        .map(|&len| act_lens[1] - len)
        .collect::<Vec<_>>();

    let mut data = Vec::new();
    if !frequencies_provided {
        data.extend(vec![
            ["音名"]
                .iter()
                .chain(steps.iter().map(|(_, name, _)| name))
                .map(std::string::ToString::to_string)
                .collect::<Vec<_>>(),
            ["孔位"]
                .iter()
                .chain(steps.iter().map(|(_, _, hole)| hole))
                .map(std::string::ToString::to_string)
                .collect::<Vec<_>>(),
        ]);
    }
    data.extend(vec![
        create_row("频率", &freqs, 1),
        create_row("等效长度", &eff_lens, 1),
        create_row("实际管长", &act_lens, 1),
        create_row("管长差值", &diffs, 1),
    ]);
    data
}

// 定义纯律音阶的倍率、音名和孔位
fn get_steps(base_name: char) -> Vec<(f64, &'static str, &'static str)> {
    match base_name {
        '5' => vec![
            (5.0 / 6.0, "低音3", "筒音"),
            (1.0, "低音5", "基音孔"),
            (9.0 / 8.0, "低音6", "第一孔"),
            (10.0 / 8.0, "低音7", "第二孔"),
            (8.0 / 6.0, "中音1", "第三孔"),
            (9.0 / 6.0, "中音2", "第四孔"),
            (10.0 / 6.0, "中音3", "第五孔"),
            (16.0 / 9.0, "中音4", ""),
            (15.0 / 8.0, "中音4#", "第六孔"),
        ],
        '2' => vec![
            (5.0 / 6.0, "低音7", "筒音"),
            (1.0, "中音2", "基音孔"),
            (9.0 / 8.0, "低音3", "第一孔"),
            (6.0 / 5.0, "低音4", "第二孔"),
            (8.0 / 6.0, "中音5", "第三孔"),
            (9.0 / 6.0, "中音6", "第四孔"),
            (10.0 / 6.0, "中音7", "第五孔"),
            (16.0 / 9.0, "高音1", ""),
            (15.0 / 8.0, "高音1#", "第六孔"),
        ],
        _ => todo!("不支持全按为{}的指法", base_name),
    }
}

// 计算吹口到音孔之间的有效长度
fn effective_length(frequency: f64) -> f64 {
    let wave_length = SOUND_SPEED / frequency;
    wave_length / 2.0
}

// 计算吹口中心到音孔边缘之间的实际长度
fn actual_length(length: f64, diameter: f64, is_tube_tone: bool) -> f64 {
    if is_tube_tone {
        length - diameter * 0.33
    } else {
        length - diameter * 4.0
    }
}

fn create_row(name: &str, numbers: &[f64], prec: usize) -> Vec<String> {
    let mut cells = Vec::with_capacity(numbers.len() + 1);
    cells.push(name.to_string());
    for &num in numbers {
        cells.push(format!("{:.1$}", num, prec));
    }
    cells
}
